package com.nbjtjc.safe.service.danger;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.nbjtjc.safe.activiti.mapper.ExtActNodeMapper;
import com.nbjtjc.safe.activiti.model.ExtActNode;
import com.nbjtjc.safe.bean.Constant;
import com.nbjtjc.safe.bean.IndexCountInfo;
import com.nbjtjc.safe.bean.Page;
import com.nbjtjc.safe.bean.Result;
import com.nbjtjc.safe.mapper.danger.DangerInspectListPublicMapper;
import com.nbjtjc.safe.mapper.danger.DangerInspectPublicMapper;
import com.nbjtjc.safe.mapper.sys.SysDeptMapper;
import com.nbjtjc.safe.mapper.sys.SysPostMapper;
import com.nbjtjc.safe.model.danger.DangerInspect;
import com.nbjtjc.safe.model.danger.DangerInspectList;
import com.nbjtjc.safe.model.danger.DangerInspectListPublic;
import com.nbjtjc.safe.model.danger.DangerInspectPublic;
import com.nbjtjc.safe.model.sys.SysDept;
import com.nbjtjc.safe.model.sys.SysPost;
import com.nbjtjc.safe.model.sys.SysUser;
import com.nbjtjc.safe.model.sys.SysUserPost;
import com.nbjtjc.safe.util.DateUtils;
import com.nbjtjc.safe.util.MyShiroUtils;
import io.swagger.models.auth.In;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.collections.map.HashedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @auther guqianbin
 * @date 2018/4/17 10:53
 **/
@Service
public class IndexService {

    private static Logger logger= LoggerFactory.getLogger(IndexService.class);

    @Autowired
    private DangerInspectListPublicMapper dangerInspectListPublicMapper;

    @Autowired
    private DangerInspectPublicMapper dangerInspectPublicMapper;

    @Autowired
    private SysDeptMapper sysDeptMapper;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ExtActNodeMapper extActNodeMapper;

    @Autowired
    private SysPostMapper sysPostMapper;

    public Result index(Page page) {
        Result result = new Result();
        Map<String, Object> data = new HashMap<>();
        int deptId = MyShiroUtils.getDeptId();
        IndexCountInfo indexCountInfo = compareIndexCountInfo(deptId, page);
        data.put("selfData", indexCountInfo == null ? new IndexCountInfo() : indexCountInfo);
        subCompanyIndexInfo(data, page);
        result.setData(data);
        return result;
    }

    /**
     * 个人岗位相关要求的统计信息
     * 例如岗位要求每月检查3次，每半年检查5次，等这类要求，统计完成次数
     *
     * @return
     */
    public Result userPostInfoCount(int year) {
        List<Date> dates = DateUtils.getYearRange(year);

        Result result = new Result();
        SysUser sysUser = MyShiroUtils.getSessionUser();
        SysPost post;
        List<SysUserPost> sysUserPosts = sysUser.getPosts();
        if (CollectionUtil.isEmpty(sysUserPosts)) {
            result.setFalse("该员工无岗位信息，无法统计岗位完成情况数据");
            post=new SysPost();
            post.setSuperviseInspectTimeLimit(Constant.SysPostTimeLimitType.MONTH.getValue());
            post.setSelfInspectTimeLimit(Constant.SysPostTimeLimitType.MONTH.getValue());
            post.setInspectCountTimeLimit(Constant.SysPostTimeLimitType.MONTH.getValue());
            post.setInspectCount(0);
            post.setSelfInspectCount(0);
            post.setSuperviseInspectCount(0);
        }else{
            SysUserPost sysUserPost = sysUserPosts.get(0);
            post=sysPostMapper.selectByPrimaryKey(sysUserPost.getPostId());
        }
        DangerInspectPublic dangerInspectPublic = new DangerInspectPublic();
        dangerInspectPublic.setStartDate(dates.get(0));
        dangerInspectPublic.setEndDate(dates.get(1));
        dangerInspectPublic.setCreateUserId(MyShiroUtils.getSessionUserId());
        List<DangerInspectPublic> dangerInspectPublicList =
                dangerInspectPublicMapper.search(dangerInspectPublic, new ArrayList<>());
        Map<Integer, Map<String, Object>> inspectTypeMap = new HashMap<>();
        countPostDangerInfo(dangerInspectPublicList,inspectTypeMap,post);
        result.setData(inspectTypeMap);
        return result;
    }

    private void countPostDangerInfo(List<DangerInspectPublic> dangerInspectPublics
            , Map<Integer, Map<String, Object>> inspectTypeMap, SysPost post) {
        Map<String, Object> selfInspectMap = new HashMap<>();
        selfInspectMap.put("typeName", "自查");
        List<Date> dates = DateUtils.getPostTimeRange(post.getSelfInspectTimeLimit());
        selfInspectMap.put("startDate",dates.get(0));
        selfInspectMap.put("endDate",dates.get(1));
        selfInspectMap.put("totalCount",post.getSelfInspectCount());
        selfInspectMap.put("count",0);
        selfInspectMap.put("timeLimitType",post.getSelfInspectTimeLimit());
        Map<String,Object> inspectMap=new HashMap<>();
        dates=DateUtils.getPostTimeRange(post.getInspectCountTimeLimit());
        inspectMap.put("startDate",dates.get(0));
        inspectMap.put("endDate",dates.get(1));
        inspectMap.put("totalCount",post.getInspectCount());
        inspectMap.put("count",0);
        inspectMap.put("typeName","排查");
        inspectMap.put("timeLimitType",post.getInspectCountTimeLimit());
        dates=DateUtils.getPostTimeRange(post.getSuperviseInspectTimeLimit());
        Map<String,Object> superviseMap=new HashMap<>();
        superviseMap.put("startDate",dates.get(0));
        superviseMap.put("endDate",dates.get(1));
        superviseMap.put("totalCount",post.getSuperviseInspectCount());
        superviseMap.put("count",0);
        inspectMap.put("typeName","检查/督查");
        superviseMap.put("timeLimitType",post.getSuperviseInspectTimeLimit());

        inspectTypeMap.put(Constant.DangerInspectType.SELF.getValue(),selfInspectMap);
        inspectTypeMap.put(Constant.DangerInspectType.COMPANYSELF.getValue(),inspectMap);
        inspectTypeMap.put(Constant.DangerInspectType.SUPERVISE.getValue(),superviseMap);

        dangerInspectPublics.forEach(inspectPublic->{
            if(inspectPublic.getType().equals(Constant.DangerInspectType.SELF.getValue())){
                if(inspectPublic.getCreateTime().after((Date) selfInspectMap.get("startDate"))
                &&inspectPublic.getCreateTime().before((Date) selfInspectMap.get("endDate"))){
                    selfInspectMap.put("count",(int)selfInspectMap.get("count")+1);
                }
            }
            if(inspectPublic.getType().equals(Constant.DangerInspectType.COMPANYSELF.getValue())){
                if(inspectPublic.getCreateTime().after((Date)inspectMap.get("startDate"))
                &&inspectPublic.getCreateTime().before((Date)inspectMap.get("endDate"))){
                    inspectMap.put("count",(int)inspectMap.get("count")+1);
                }
            }
            if(inspectPublic.getType().equals(Constant.DangerInspectType.SUPERVISE.getValue())){
                if(inspectPublic.getCreateTime().after((Date)superviseMap.get("startDate"))
                        &&inspectPublic.getCreateTime().before((Date)superviseMap.get("endDate"))){
                    superviseMap.put("count",(int)superviseMap.get("count")+1);
                }
            }
        });
    }

    public Map<String, Integer> dangerTaskCount(int companyId) {
        Map<String, Integer> countData = new HashMap<>();
        initDangerTaskCountMap(countData);
        List<Integer> subDeptIds = sysDeptMapper.getSubCompanyId(companyId
                , Constant.SysDeptType.DEPT.getValue());
        if (CollectionUtil.isEmpty(subDeptIds)) {
            subDeptIds = new ArrayList<>();
        }
        subDeptIds.add(companyId);
        List<DangerInspectPublic> dangerInspectPublics = dangerInspectPublicMapper
                .findInspectPublicByDeptIdsUnFinish(subDeptIds);
        if (CollectionUtil.isEmpty(dangerInspectPublics)) {
            return countData;
        }
        Set<String> processInstanceIds =
                dangerInspectPublics.stream()
                        .map(DangerInspectPublic::getProcessInstanceId)
                        .collect(Collectors.toSet());
        List<ProcessInstance> processInstances =
                runtimeService.createProcessInstanceQuery()
                        .processInstanceIds(processInstanceIds).list();
        Set<String> procDefIdsSet = processInstances.stream()
                .map(ProcessInstance::getProcessDefinitionId)
                .collect(Collectors.toSet());
        List<String> procDefIds = new ArrayList<>(procDefIdsSet);
        //根据流程初始化统计信息的类别，根据流程的任务名作为类别，可能出现多个版本流程定义的id
        if (CollectionUtil.isNotEmpty(procDefIds)) {
            procDefIds.forEach(procDefId -> {
                ExtActNode extActNode = new ExtActNode();
                extActNode.setProcdefId(procDefId);
                List<ExtActNode> extActNodes = extActNodeMapper.select(extActNode);
                if (CollectionUtil.isNotEmpty(extActNodes)) {
                    extActNodes.forEach(node -> {
                        if (!countData.containsKey(node.getName())) {
                            countData.put(node.getName(), 0);
                        }
                    });
                }
            });
        }
        List<String> pIds = new ArrayList<>(processInstanceIds);
        if (CollectionUtil.isEmpty(pIds)) {
            return countData;
        }
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceIdIn(pIds).list();
        if (CollectionUtil.isNotEmpty(tasks)) {
            tasks.forEach(task -> {
                if (countData.containsKey(task.getName())) {
                    countData.put(task.getName(), countData.get(task.getName()) + 1);
                } else {
                    countData.put(task.getName(), 1);
                }
            });
        }
        return countData;
    }

    public List<DangerInspectListPublic> latestDanger(int companyId) {
        List<Integer> subDeptIds = sysDeptMapper.getSubCompanyId(companyId
                , Constant.SysDeptType.DEPT.getValue());
        if (CollectionUtil.isEmpty(subDeptIds)) {
            subDeptIds = new ArrayList<>();
        }
        subDeptIds.add(companyId);
        List<DangerInspectPublic> dangerInspectPublics = dangerInspectPublicMapper
                .findInspectPublicByDeptIdsUnFinish(subDeptIds);
        if (CollectionUtil.isEmpty(dangerInspectPublics)) {
            return new ArrayList<>();
        }
        Map<Integer, DangerInspectPublic> dangerPublicMap = dangerInspectPublics.stream()
                .collect(Collectors.toMap(DangerInspectPublic::getId, dangerInspectPublic -> dangerInspectPublic));
        List<Integer> dangerInspectIds = dangerInspectPublics.stream()
                .map(DangerInspectPublic::getId).collect(Collectors.toList());
        DangerInspectListPublic dangerInspectListPublic = new DangerInspectListPublic();
        dangerInspectListPublic.setDangerInspectPublicIds(dangerInspectIds);
        dangerInspectListPublic.setNeedChange(Constant.NeedChange.NEED_CHANGE.getValue());
        List<DangerInspectListPublic> dangerInspectListPublics =
                dangerInspectListPublicMapper.search(dangerInspectListPublic);
        if (CollectionUtil.isEmpty(dangerInspectListPublics)) {
            return new ArrayList<>();
        }
        dangerInspectListPublics.forEach(dangerList -> {
            dangerList.setDeptName(dangerPublicMap.get(dangerList.getInspectPublicId()).getTargetDeptName());
        });

        return dangerInspectListPublics;
    }

    public Map<String, Object> indexChartData(int year, int companyId) {
        Map<String, Object> data = new HashedMap();
        List<Date> dateRanges = DateUtils.getYearRange(year);
        Date startDate = dateRanges.get(0);
        Date endDate = dateRanges.get(1);
        List<Integer> subDeptIds = sysDeptMapper.getSubCompanyId(companyId
                , Constant.SysDeptType.DEPT.getValue());
        if (CollectionUtil.isEmpty(subDeptIds)) {
            subDeptIds = new ArrayList<>();
        }
        subDeptIds.add(companyId);
        List<DangerInspectPublic> dangerInspectPublics = dangerInspectPublicMapper
                .findInspectPublicByDeptIds(subDeptIds, startDate, endDate);
        if (CollectionUtil.isEmpty(dangerInspectPublics)) {
            return data;
        }
        List<Integer> dangerInspectIds = dangerInspectPublics.stream()
                .map(DangerInspectPublic::getId).collect(Collectors.toList());
        Map<Integer, DangerInspectPublic> dangerPublicMap = dangerInspectPublics.stream()
                .collect(Collectors.toMap(DangerInspectPublic::getId, dangerInspectPublic -> dangerInspectPublic));
        DangerInspectListPublic dangerInspectListPublic = new DangerInspectListPublic();
        dangerInspectListPublic.setDangerInspectPublicIds(dangerInspectIds);
        dangerInspectListPublic.setNeedChange(Constant.NeedChange.NEED_CHANGE.getValue());
        List<DangerInspectListPublic> dangerInspectListPublics =
                dangerInspectListPublicMapper.search(dangerInspectListPublic);
        if (CollectionUtil.isEmpty(dangerInspectIds)) {
            return data;
        }
        dangerInspectListPublics.forEach(inspectListPublic -> {
            dangerPublicMap.get(inspectListPublic.getInspectPublicId())
                    .getDangerInspectListPublicList().add(inspectListPublic);
        });
        Map<Integer, Map<String, Object>> deptCountMap = new HashMap<>();
        Map<String, Map<String, Object>> dangerLevelCountMap = new HashMap<>();
        initDeptCountMap(subDeptIds, deptCountMap);
        dangerInspectPublics.forEach(dangerInspectPublic -> {
            List<DangerInspectListPublic> inspectListPublicList = dangerInspectPublic.getDangerInspectListPublicList();
            if (CollectionUtil.isNotEmpty(inspectListPublicList)) {
                if (dangerInspectPublic.getFinish()) {
                    countDangerLevel(dangerInspectPublic, dangerLevelCountMap, deptCountMap, true);
                } else {
                    countDangerLevel(dangerInspectPublic, dangerLevelCountMap, deptCountMap, false);
                }
            }
        });

        data.put("dangerLevelCountMap", new ArrayList<>(dangerLevelCountMap.values()));
        data.put("deptCountMap", new ArrayList<>(deptCountMap.values()));

        return data;
    }

    private void initDeptCountMap(List<Integer> deptIds, Map<Integer, Map<String, Object>> deptCountMap) {
        List<SysDept> sysDepts = sysDeptMapper.getSysDeptName(deptIds);
        Map<Integer, String> deptIdNameMap = sysDepts.stream()
                .collect(Collectors.toMap(SysDept::getId, SysDept::getName));
        sysDepts.forEach(sysDept -> {
            Map<String, Object> data = new HashMap<>();
            data.put("deptName", sysDept.getName());
            data.put("已整改", 0);
            data.put("未整改", 0);

            deptCountMap.put(sysDept.getId(), data);
        });

    }

    /**
     * @param dangerInspectPublic
     * @param dangerLevelCountMap
     * @param deptCountMap
     * @param finish
     */
    private void countDangerLevel(DangerInspectPublic dangerInspectPublic
            , Map<String, Map<String, Object>> dangerLevelCountMap
            , Map<Integer, Map<String, Object>> deptCountMap, boolean finish) {
        if (deptCountMap.containsKey(dangerInspectPublic.getTargetDeptId())) {
            Map<String, Object> data = deptCountMap.get(dangerInspectPublic.getTargetDeptId());
            if (finish) {
                data.put("已整改", (int) data.get("已整改") + dangerInspectPublic.getDangerInspectListPublicList().size());
            } else {
                data.put("未整改", (int) data.get("未整改") + dangerInspectPublic.getDangerInspectListPublicList().size());
            }
        } else {
            Map<String, Object> data = new HashMap<>();
            data.put("deptName", dangerInspectPublic.getTargetDeptName());
            data.put("已整改", 0);
            data.put("未整改", 0);
            if (finish) {
                data.put("已整改", (int) data.get("已整改") + dangerInspectPublic.getDangerInspectListPublicList().size());
            } else {
                data.put("未整改", 1);
            }
            deptCountMap.put(dangerInspectPublic.getTargetDeptId(), data);
        }
        dangerInspectPublic.getDangerInspectListPublicList().forEach(inspectList -> {
            if (dangerLevelCountMap.containsKey(inspectList.getHiddenDangerLevel())) {
                if (finish) {
                    dangerLevelCountMap.get(inspectList.getHiddenDangerLevel())
                            .put("已整改"
                                    , (int) dangerLevelCountMap
                                            .get(inspectList.getHiddenDangerLevel()).get("已整改") + 1);
                } else {
                    dangerLevelCountMap.get(inspectList.getHiddenDangerLevel())
                            .put("未整改"
                                    , (int) dangerLevelCountMap
                                            .get(inspectList.getHiddenDangerLevel()).get("未整改") + 1);
                }
            } else {
                Map<String, Object> levelCountMap = new HashMap<>();
                levelCountMap.put("已整改", 0);
                levelCountMap.put("未整改", 0);
                levelCountMap.put("等级", inspectList.getHiddenDangerLevel());
                if (finish) {
                    levelCountMap.put("已整改", 1);
                } else {
                    levelCountMap.put("未整改", 1);
                }
                dangerLevelCountMap.put(inspectList.getHiddenDangerLevel(), levelCountMap);
            }

        });
    }

    private void initDangerTaskCountMap(Map<String, Integer> countData) {
        List<ExtActNode> extActNodes = new ArrayList<>();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("emgFlow1")
                .latestVersion().singleResult();
        if (ObjectUtil.isNull(processDefinition)) {
            return;
        }
        ExtActNode extActNode = new ExtActNode();
        extActNode.setProcdefId(processDefinition.getId());
        extActNodes = extActNodeMapper.select(extActNode);
        if (CollectionUtil.isNotEmpty(extActNodes)) {
            return;
        }
        extActNodes.forEach(node -> {
            countData.put(node.getName(), 0);
        });
    }

    private void subCompanyIndexInfo(Map<String, Object> data, Page page) {
        SysDept sysDept = new SysDept();
        sysDept.setParentId(MyShiroUtils.getDeptId());
        sysDept.setType(1);
        List<SysDept> subComapnys = sysDeptMapper.search(sysDept, null);
        if (CollectionUtil.isEmpty(subComapnys)) {
            return;
        }
        List<IndexCountInfo> indexCountInfos = new ArrayList<>();
        subComapnys.forEach(company -> {
            IndexCountInfo indexCountInfo = compareIndexCountInfo(company.getId(), page);
            if (ObjectUtil.isNotNull(indexCountInfo)) {
                indexCountInfo.setSysDept(company);
                indexCountInfos.add(indexCountInfo);
            }
        });
        data.put("subCompanies", indexCountInfos);
    }


    private IndexCountInfo compareIndexCountInfo(int deptId, Page page) {
        DangerInspectPublic dangerInspectPublic = new DangerInspectPublic();
        dangerInspectPublic.setPublicDeptId(deptId);
        dangerInspectPublic.setEndDate(page.getEndDate());
        dangerInspectPublic.setStartDate(page.getStartDate() == null
                ? new Date() : page.getStartDate());
        List<DangerInspectPublic> dangerInspectPublics =
                dangerInspectPublicMapper.search(dangerInspectPublic, null);
        if (CollectionUtil.isEmpty(dangerInspectPublics)) {
            return new IndexCountInfo();
        }
        DangerInspectListPublic dangerInspectListPublic = new DangerInspectListPublic();
        dangerInspectListPublic.setNeedChange(1);//查询需要整改的
        dangerInspectPublics.forEach(danger -> {
            dangerInspectListPublic.getDangerInspectPublicIds().add(danger.getId());
        });
        List<DangerInspectListPublic> dangerInspectListPublicList =
                dangerInspectListPublicMapper.search(dangerInspectListPublic);
        if (CollectionUtil.isNotEmpty(dangerInspectListPublicList)) {
            return new IndexCountInfo();
        }
        IndexCountInfo indexCountInfo = new IndexCountInfo();
        indexCountInfo.setDangerCount(dangerInspectListPublicList.size());
        indexCountInfo.setInspectPublicCount(dangerInspectPublics.size());
        Map<Integer, DangerInspectPublic>
                dangerInspectPublicMap = dangerInspectPublics.stream().collect(Collectors.toMap(
                DangerInspectPublic::getId, dangerInspectPublic1 -> dangerInspectPublic1));
        dangerInspectListPublicList.forEach(listPublic -> {
            dangerInspectPublicMap.get(listPublic.getInspectPublicId())
                    .getDangerInspectListPublicList().add(listPublic);
        });
        dangerInspectPublicMap.entrySet().forEach(entry -> {
            int inspectPublicStatus = entry.getValue().getStatus();
            if (inspectPublicStatus <= Constant.InspectPublicStatus.INSPECT_UNEXAMINE.getValue()) {
                indexCountInfo.setUnStartInspectPublicCount(indexCountInfo.getUnStartInspectPublicCount() + 1);
            }
            //当前状态是已检查和已整改之间的状态，没有整改完成的范围
            if (inspectPublicStatus > Constant.InspectPublicStatus.FINISH_INSPECT.getValue()
                    && inspectPublicStatus < Constant.InspectPublicStatus.FINISH_RECTIFY.getValue()) {
                indexCountInfo.setUnfinishDangerCount(indexCountInfo.getUnfinishDangerCount() +
                        entry.getValue().getDangerInspectListPublicList().size());
            }
            //已经整改完成
            if (inspectPublicStatus == Constant.InspectPublicStatus.FINISH_RECTIFY.getValue()) {
                indexCountInfo.setFinishDangerCount(indexCountInfo.getFinishDangerCount() +
                        entry.getValue().getDangerInspectListPublicList().size());
            }
        });

        return indexCountInfo;
    }
}
